<?php

Raise::load('core.RaiseObject');
Raise::load('core.Url');
Raise::load('core.RaiseUIControl');
Raise::load('core.RaiseHtmlTag');
Raise::load('core.RaiseVariable');

/**
 * RaisePagination helper class
 * Creates pagination with links for navgiation from page to page
 * e.g. Previous 1 ... 5 6 7 8 9 ... 16 Next
 *
 * @author Sam-Mauris Yong <hellclanner at live dot com>
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.Core.Helper
 * @since 1.2
 */
class RaisePagination extends RaiseObject {

    /**
     * The URL to put for pagination
     * @var string|Url
     */
    private $url;
    
    /**
     * The total number of items
     * @var integer
     */
    private $totalItem = 0;

    /**
     * The number of entries per page
     * @var integer
     */
    private $itemsPerPage = 10;

    /**
     * The current page
     * @var integer
     */
    private $current = 0;

    /**
     * Flag whether to show the Previous and Next links
     * Defaults to TRUE
     * @var boolean
     */
    private $showPreviousNext = true;

    /**
     * Show "..." if difference between current page and max/min page is more than $interval
     * @var integer
     */
    private $interval = 5;

    /**
     * The UI text for Next link
     * @var string|RaiseUIControl
     */
    private $nextLabel = 'Next';

    /**
     * The UI text for Previous link
     * @var string|RaiseUIControl
     */
    private $previousLabel = 'Previous';

    /**
     * Get or set the URL for each page link
     * @param string $u (optional) If $u is an integer, the method will return the page link for the page. If it is a string, the new URL will be set to $u.
     * @return string
     */
    public function url($u = null) {
        if (func_num_args() == 1) {
            $this->url = $u;
        }
        return $this->url;
    }

    public function parseUrl($x) {
        return sprintf($this->url, $x);
    }

    /**
     * Get or set the total number of items
     * @param integer $t (optional) The new value to set
     * @return integer
     */
    public function totalItem($t = null) {
        if (func_num_args() == 1) {
            $this->totalItem = $t;
        }
        return $this->totalItem;
    }

    /**
     * Get or set the number of items on each page
     * @param integer $p (optional) Set the new value
     * @return integer
     */
    public function itemsPerPage($p = null) {
        if (func_num_args() == 1) {
            $this->itemsPerPage = $p;
        }
        return $this->itemsPerPage;
    }

    /**
     * Get or set the current page number, starting from 1
     * @param integer $c (optional) The new page number
     * @return integer
     */
    public function current($c = null) {
        if (func_num_args() == 1) {
            $this->current = $c;
        }
        return $this->current;
    }

    /**
     * Get or set whether to show previous / next links for pagination
     * @param boolean $s (optional) The new flag value
     * @return boolean
     */
    public function showPreviousNext($s = null) {
        if (func_num_args() == 1) {
            $this->showPreviousNext = $s;
        }
        return $this->showPreviousNext;
    }

    /**
     * Get or set the value where pagination will show "..." if difference between current page and max/min page is more than $interval
     * @param integer $i (optional) The new interval value
     * @return integer
     */
    public function interval($i = null) {
        if (func_num_args() == 1) {
            $this->interval = $i;
        }
        return $this->interval;
    }

    /**
     * Get or set the text shown in the Next link
     * @param string $n (optional) The new text label for Next link
     * @return string
     */
    public function nextLabel($n = null) {
        if (func_num_args() == 1) {
            $this->nextLabel = $n;
        }
        return $this->nextLabel;
    }

    /**
     * Get or set the text shown in the Previous link
     * @param string $p (optional) The new text label for Previous link
     * @return string
     */
    public function previousLabel($p = null) {
        if (func_num_args() == 1) {
            $this->previousLabel = $p;
        }
        return $this->previousLabel;
    }

    /**
     * Constructs the pagination object
     */
    public function __construct($url = null) {
        if (func_num_args() == 1) {
            $this->url = $url;
        }
    }

    /**
     * Get the RaiseHtmlTag that represents the entire pagination
     * @return RaiseHtmlTag
     */
    public function html() {
        $min = 1;
        $max = $this->totalPages();

        if ($max < $min) {
            RaiseVariable::flip($min, $max);
        }

        if ($max == 0) {
            return '';
        }

        if ($this->current < $min || $this->current > $max) {
            $this->current = $min;
        }

        $pagination = new RaiseHtmlTag('span', '', array('class' => 'pagination'));

        if ($this->showPreviousNext()) {
            $pagination->value($pagination->value() . $this->previousLink());
        }

        // show minimum
        if ($this->current - $this->interval > $min) {
            $pagination->value($pagination->value() . $this->link($min));
            if ($this->current - ($this->interval + 1) > $min) {
                $pagination->value($pagination->value() . '<span>...</span>');
            }
        }

        for ($x = ($this->current - $this->interval > 1 ? $this->current - $this->interval : $min); $x >= $min && $x <= $max && ($x < $this->current); $x++) {
            $pagination->value($pagination->value() . $this->link($x));
        }

        $currentLinkTag = $this->link($this->current);
        $currentLinkTag->attributes()->add('class', 'current');
        $pagination->value($pagination->value() . $currentLinkTag);

        for ($x = ($this->current + 1); $x >= $min && $x <= $max && ($x <= $this->current + $this->interval); $x++) {
            $pagination->value($pagination->value() . $this->link($x));
        }

        // show maximum
        if ($this->current + $this->interval < $max) {
            if ($this->current + ($this->interval + 1) < $max) {
                $pagination->value($pagination->value() . '<span>...</span>');
            }
            $pagination->value($pagination->value() . $this->link($max));
        }

        if ($this->showPreviousNext()) {
            $pagination->value($pagination->value() . $this->nextLink());
        }

        return $pagination;
    }

    /**
     * Get the HTML link for page $x
     * @param integer $x
     * return string
     */
    public function link($x) {
        $a = new RaiseHtmlTag('a', $x, array('href' => $this->parseUrl($x)));
        return $a;
    }

    /**
     * Get the URL of the previous page
     * @return string
     */
    public function previousUrl() {
        if ($this->current() > 1 && $this->showPreviousNext()) {
            return $this->parseUrl($this->current() - 1);
        }
        return null;
    }

    /**
     * Get the HTML link of the previous page
     * @return RaiseHtmlTag
     */
    public function previousLink() {
        if ($this->current() > 1 && $this->showPreviousNext()) {
            $a = new RaiseHtmlTag('a', $this->previousLabel(), array('class' => 'previous', 'href' => $this->previousUrl()));
            return $a;
        }
        return null;
    }

    /**
     * Get the total number of pages
     * @return integer
     */
    public function totalPages() {
        return ceil($this->totalItem() / $this->itemsPerPage());
    }

    /**
     * Get the URL of the next page
     * @return string
     */
    public function nextUrl() {
        if ($this->current() < $this->totalPages() && $this->showPreviousNext()) {
            return $this->parseUrl($this->current() + 1);
        }
        return null;
    }

    /**
     * Get the HTML link of the next page
     * @return RaiseHtmlTag
     */
    public function nextLink() {
        if ($this->current() < $this->totalPages() && $this->showPreviousNext()) {
            $a = new RaiseHtmlTag('a', $this->nextLabel(), array('class' => 'next', 'href' => $this->nextUrl()));
            return $a;
        }
        return null;
    }

    public function __toString() {
        return $this->html();
    }

}